/*
 * Copyright 2010 - 2011, The PLDesktop Development Team
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#ifndef PLFLAGS_H
#define PLFLAGS_H

namespace PLSDK {

/**
  * Declare a enumeration as flag.
  *
  * @ingroup tools_core
  */
#define PLDECLAREFLAGS(Flags, Enum) \
    typedef PLFlags<Enum> Flags

/**
  * Create operators for a exported flag.
  *
  * @ingroup tools_core
  */
#define PLDECLAREOPERATORSFORFLAGS(Flags) \
    inline PLFlags<Flags::enum_type> operator|(Flags::enum_type f1, Flags::enum_type f2) \
      { return PLFlags<Flags::enum_type>(f1) | f2; } \
    inline PLFlags<Flags::enum_type> operator|(Flags::enum_type f1, PLFlags<Flags::enum_type> f2) \
      { return f2 | f1; }

/**
  * Class to manage enums as flag.
  *
  * @ingroup tools_core
  */
template < typename Enum >
class PLFlags {
   typedef void **Zero;
public:
   /**
     * The enumeration that was managed with the PFlags class.
     */
   typedef Enum enum_type;
   /**
     * Copy constructor.
     *
     * @param f Flags to copy.
     */
   inline PLFlags(const PLFlags &f);
   /**
     * Copy a enumeration.
     *
     * @param f Enumeration to copy.
     */
   inline PLFlags(Enum f);
   /**
     * Create a empty Flags instance.
     */
   inline PLFlags(Zero = 0) : i(0) {}
   /**
     * Create a flag with an int value.
     *
     * @param f Int value to set.
     */
   inline PLFlags(int f);
   /**
     * = operator to set frags to this flags instance.
     */
   inline PLFlags &operator=(const PLFlags &f);
   /**
     * Set a mask over the flags.
     */
   inline PLFlags &operator&=(int mask);
   /**
     * Add flags to this flags.
     */
   inline PLFlags &operator|=(PLFlags f);
   /**
     * Add a enum to this Flags.
     */
   inline PLFlags &operator|=(Enum f);
   /**
     * XOR the flags.
     */
   inline PLFlags &operator^=(PLFlags f);
   /**
     * XOR the flags.
     */
   inline PLFlags &operator^=(Enum f);
   /**
     * Return the int value of the flags.
     */
   inline operator int() const;
   /**
     * OR the flags.
     */
   inline PLFlags operator|(PLFlags f) const;
   /**
     * OR the flags.
     */
   inline PLFlags operator|(Enum f) const;
   /**
     * XOR the flags.
     */
   inline PLFlags operator^(PLFlags f) const;
   /**
     * XOR the flags.
     */
   inline PLFlags operator^(Enum f) const;
   /**
     * AND the flags.
     */
   inline PLFlags operator&(int mask) const;
   /**
     * AND the flags.
     */
   inline PLFlags operator&(Enum f) const;
   /**
     * Bitwise negation the flags.
     */
   inline PLFlags operator~() const;
   /**
     * Returns true if no flag is set.
     */
   inline bool operator!() const;
   /**
     * Test for the flags.
     */
   inline bool testFlag(Enum f) const;
private:
   int i;
};

template < typename Enum >
PLFlags<Enum>::PLFlags(const PLFlags &f) : i(f.i) {
}

template < typename Enum >
PLFlags<Enum>::PLFlags(Enum f) : i(f) {
}

template < typename Enum >
PLFlags<Enum>::PLFlags(int f) : i(f) {
}

template < typename Enum >
PLFlags<Enum> &PLFlags<Enum>::operator=(const PLFlags<Enum> &f) {
   i = f.i;
   return *this;
}

template < typename Enum >
PLFlags<Enum> &PLFlags<Enum>::operator&=(int mask) {
   i &= mask;
   return *this;
}

template < typename Enum >
PLFlags<Enum> &PLFlags<Enum>::operator|=(PLFlags<Enum> f) {
   i |= f.i;
   return *this;
}

template < typename Enum >
PLFlags<Enum> &PLFlags<Enum>::operator|=(Enum f) {
   i |= f;
   return *this;
}

template < typename Enum >
PLFlags<Enum> &PLFlags<Enum>::operator^=(PLFlags<Enum> f) {
   i ^= f.i;
   return *this;
}

template < typename Enum >
PLFlags<Enum> &PLFlags<Enum>::operator^=(Enum f) {
   i ^= f;
   return *this;
}

template < typename Enum >
PLFlags<Enum>::operator int() const {
   return i;
}

template < typename Enum >
PLFlags<Enum> PLFlags<Enum>::operator|(PLFlags<Enum> f) const {
   PLFlags<Enum> g;
   g.i = i | f.i;
   return g;
}

template < typename Enum >
PLFlags<Enum> PLFlags<Enum>::operator|(Enum f) const {
   PLFlags<Enum> g;
   g.i = i | f;
   return g;
}

template < typename Enum >
PLFlags<Enum> PLFlags<Enum>::operator^(PLFlags<Enum> f) const {
   PLFlags<Enum> g;
   g.i = i ^ f.i;
   return g;
}

template < typename Enum >
PLFlags<Enum> PLFlags<Enum>::operator^(Enum f) const {
   PLFlags<Enum> g;
   g.i = i ^ f;
   return g;
}

template < typename Enum >
PLFlags<Enum> PLFlags<Enum>::operator&(int mask) const {
   PLFlags<Enum> g;
   g.i = i & mask;
   return g;
}

template < typename Enum >
PLFlags<Enum> PLFlags<Enum>::operator&(Enum f) const {
   PLFlags<Enum> g;
   g.i = i & f;
   return g;
}

template < typename Enum >
PLFlags<Enum> PLFlags<Enum>::operator~() const {
   PLFlags<Enum> g;
   g.i = ~i;
   return g;
}

template < typename Enum >
bool PLFlags<Enum>::operator!() const {
   return !i;
}

template < typename Enum >
bool PLFlags<Enum>::testFlag(Enum f) const {
   return (i & f) == f;
}

}

#endif // PLFLAGS_H
